package xyz.perkd.process.impl;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.*;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDStream;
import xyz.perkd.model.POTicket;
import xyz.perkd.process.IProcessor;
import xyz.perkd.util.DateTimeUtil;
import xyz.perkd.util.WaterMaker;

import javax.swing.*;
import java.io.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Processor implements IProcessor {

    public IProcessor.ProcessListener stateUpdate;
    public String path;

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    public void process(String path, ProcessListener stateUpdate) {
        this.stateUpdate = stateUpdate;
        this.path = path;
        executorService.submit(this::start);
    }

    private void notify(String message) {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onProcess(message, 0);
            }
        });
    }

    private void notifyWarn(String message) {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onProcess(message, 1);
            }
        });
    }

    private void notifyErr(String message) {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onProcess(message, 2);
            }
        });
    }

    private void start() {
        SwingUtilities.invokeLater(() -> {
            if (stateUpdate != null) {
                stateUpdate.onStart();
            }
        });
        File file = new File(path);
        String workDir = file.getParent();
        String outputPath = workDir + "/" + file.getName() + "_output";
        try {
            PDDocument document = Loader.loadPDF(file);
            File outputDir = new File(outputPath);
            if (!outputDir.exists()) {
                outputDir.mkdir();
            }
            for (int i = 0; i < document.getNumberOfPages(); i++) {
                notify("开始处理第" + (i + 1) + "页");
                PDDocument extractedDocument = new PDDocument();
                PDPage page = document.getPage(i);
                pageReady(i, document, page, outputPath);
                addWaterMark(i, document, page);
                extractedDocument.addPage(page);
                File outputFile = new File(outputDir, i + ".pdf");
                extractedDocument.save(outputFile);
                notify("完成处理第" + (i + 1) + "页");
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            SwingUtilities.invokeLater(() -> {
                if (stateUpdate != null) {
                    stateUpdate.onFinish(outputPath);
                }
            });
        }
    }


    private void pageReady(int idx, PDDocument document, PDPage page, String outputPath) {
        PDFStreamParser pdfsp = null;
        try {
            BufferedWriter tokensWriter = new BufferedWriter(new FileWriter(new File(outputPath, "tokens" + idx + ".txt")));
            BufferedWriter stringsWriter = new BufferedWriter(new FileWriter(new File(outputPath, "strings" + idx + ".txt")));
            pdfsp = new PDFStreamParser(page);
            List<Object> tokens = pdfsp.parse();

            Iterator<Object> iterator = tokens.iterator();
            while (iterator.hasNext()) {
                Object token = iterator.next();
                System.out.println(token.getClass() + " -> " + token);
                tokensWriter.write(token.getClass() + " -> " + token + "\n");
                Class clazz = token.getClass();
                if (clazz.equals(Operator.class)) {
                } else if (clazz.equals(COSName.class)) {
                } else if (clazz.equals(COSInteger.class)) {
                } else if (clazz.equals(COSString.class)) {
                    stringsWriter.write(token.getClass() + " -> " + token + "\n");
                    COSString item = (COSString) token;
                    if (item.getString().contains("$")) {
                        iterator.remove();
                        notifyWarn("第" + (idx + 1) + "页 移除金额内容 " + item.getString());
                    } else if (item.getString().matches("^\\d{2}/\\d{2}/\\d{2}.*")) {
                        String origin = item.getString();
                        String fixed = DateTimeUtil.offsetDate(item.getString(), -10);
                        item.setValue(fixed.getBytes());
                        notifyWarn("第" + (idx + 1) + "页 减少时间10天 " + origin + " -> " + fixed);
                    }
                } else if (clazz.equals(COSFloat.class)) {
                } else if (clazz.equals(COSArray.class)) {
                    stringsWriter.write(token.getClass() + " -> " + token + "\n");
                }
            }
            tokensWriter.close();
            stringsWriter.close();

            PDStream updatedStream = new PDStream(document);
            OutputStream out = updatedStream.createOutputStream(COSName.FLATE_DECODE);
            ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
            tokenWriter.writeTokens(tokens);
            out.close();
            page.setContents(updatedStream);


        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public void addWaterMark(int idx, PDDocument document, PDPage page) {
        WaterMaker.addWaterMark(idx, document, page, "HENG HANG TRADING CO.,TLD");
    }
}
